/*************************************************************************
 * Nuvoton Electronics Corporation confidential
 *
 * Copyright (c) 2008 by Nuvoton Electronics Corporation
 * All rights reserved
 *
 * FILENAME
 *     CY3684_isoloop.c
 *
 * VERSION
 *     1.0
 *
 * DESCRIPTION
 *     NUC900 USB Host isochronous loop back test program
 *
 * HISTORY
 *     2008.06.24       Created
 *
 * REMARK
 *     None
 **************************************************************************/
#ifdef ECOS
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "kapi.h"
#include "diag.h"
#include "wbtypes.h"
#include "wbio.h"
#define printf  diag_printf
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wbio.h"
#include "wblib.h"
#define printf  sysprintf
#endif
#include "nuc900_reg.h"
#include "usb.h"

//#define SYSTEM_CLOCK		18432000
//#define SYSTEM_CLOCK		16670000
//#define SYSTEM_CLOCK		15000000
#define SYSTEM_CLOCK		14318000
#define UART_BAUD_RATE		115200

#ifdef ECOS
#define sysGetTicks(TIMER0)   cyg_current_time()
#endif


#define FRAMES_PER_DESC		8
#define TRANSFER_BUFF_SIZE	(FRAMES_PER_DESC * 3072 * 4)
#define COMPARE_BUFF_SIZE   (FRAMES_PER_DESC * 3072 * 16)

USB_DEV_T  *_ptCY3684Dev = NULL;

static URB_T  tUrbIsoIn, tUrbIsoOut;

volatile INT   	_nTestPacketLen = 512;
volatile UINT32	_uDataCountIN=0, _uDataCountOUT=0;
volatile INT	_nTestCycle = 0;

__align(4096) UINT8  _BufferOutNonCache[TRANSFER_BUFF_SIZE];
__align(4096) UINT8  _BufferInNonCache[TRANSFER_BUFF_SIZE];

UINT8  *_u8DataBufferOut, *_u8DataBufferIn;

__align(32) UINT8  _u8CompareBuff[COMPARE_BUFF_SIZE];
volatile static INT _nCmpBuffHead, _nCmpBuffTail;


static VOID  iso_in_irq(URB_T *urb)
{
	INT  j, k;
	
    //printf("iso_in_irq - %d\n", urb->actual_length);

    if (urb->status) 
        printf("iso_in_irq, tUrbIsoIn error:%d, %d\n", urb->status, urb->actual_length);

   	for (j = 0; j < FRAMES_PER_DESC; j++) 
		_uDataCountIN += urb->iso_frame_desc[j].actual_length;		    		

	memset((UINT8 *)urb, 0, sizeof(urb));
    urb->dev = _ptCY3684Dev;
    urb->context = (VOID *)_ptCY3684Dev;
    urb->pipe = usb_rcvisocpipe(_ptCY3684Dev, 6);
    urb->transfer_flags = USB_ISO_ASAP;
    urb->transfer_buffer = (UINT8 *)((UINT32)_u8DataBufferIn);
    urb->complete = iso_in_irq;
    urb->number_of_packets = FRAMES_PER_DESC;
    urb->transfer_buffer_length = 3072 * FRAMES_PER_DESC;
	urb->interval = 8;

    for (j=k=0; j < FRAMES_PER_DESC; j++, k += _nTestPacketLen) 
    {
        urb->iso_frame_desc[j].status = 0;
        urb->iso_frame_desc[j].actual_length = 0;
        urb->iso_frame_desc[j].offset = k;
        urb->iso_frame_desc[j].length = _nTestPacketLen;
    }

    if (USB_SubmitUrb(urb))
    	printf("\nsubmit irq in - failed!\n");
}


static VOID  iso_out_irq(URB_T *urb)
{
	INT		index, j, k, status;
    
    //printf("iso_out_irq\n");

    if (urb->status)
        printf("iso_out_irq, error:%d, %d\n", urb->status, urb->actual_length);

   	_uDataCountOUT += _nTestPacketLen * FRAMES_PER_DESC;
   	urb->dev = _ptCY3684Dev;
	urb->transfer_buffer_length = _nTestPacketLen;
   	urb->pipe = usb_sndisocpipe(_ptCY3684Dev, 2);
   	urb->transfer_flags = USB_ISO_ASAP;
   	urb->transfer_buffer = (UINT8 *)((UINT32)_u8DataBufferOut);
   	urb->complete = iso_out_irq;
   	urb->number_of_packets = FRAMES_PER_DESC;
   	urb->transfer_buffer_length = 3072 * FRAMES_PER_DESC;
	urb->interval = 8;

   	for (j=k=0; j < FRAMES_PER_DESC; j++, k += _nTestPacketLen) 
   	{
       	urb->iso_frame_desc[index].status = 0;
       	urb->iso_frame_desc[index].actual_length = 0;
        urb->iso_frame_desc[j].offset = k;
        urb->iso_frame_desc[j].length = _nTestPacketLen;
		memcpy(&_u8DataBufferOut[k], 
				(UINT8 *)(0x8000+_nCmpBuffTail), 
				_nTestPacketLen);
		memcpy(&_u8CompareBuff[_nCmpBuffTail], 
				(UINT8 *)(0x8000+_nCmpBuffTail), 
				_nTestPacketLen);
		_nCmpBuffTail = (_nCmpBuffTail+_nTestPacketLen) % COMPARE_BUFF_SIZE;
    }
   	status = USB_SubmitUrb(urb);
    if (status)
   		printf("\nsubmit iso out - failed! (%d)\n", status);
}


static VOID *isoloop_probe(USB_DEV_T *dev, UINT32 ifnum,
                           const USB_DEV_ID_T *id)
{
    if ((dev->descriptor.idVendor != 0x04B4) ||
        (dev->descriptor.idProduct != 0x1006))
         return NULL;
             
    printf("isoloop_probe on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum);

    _ptCY3684Dev = dev;

    printf("\nisoloop_probe - OK!\n");
    return (VOID *)_ptCY3684Dev;
}


static VOID isoloop_disconnect(USB_DEV_T *dev, VOID *ptr)
{
    USB_UnlinkUrb(&tUrbIsoIn);
    USB_UnlinkUrb(&tUrbIsoOut);
}


static USB_DEV_ID_T isoloop_id_table[] = 
{
    USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT,   /* match_flags */
    0x04B4, 0x1006, 0, 0, 0, 0, 0,
    0,                   /* bInterfaceClass */
    0, 0, 0 
};

USB_DRIVER_T  isoloop_driver = 
{
    "ISOLOOP",
    isoloop_probe,
    isoloop_disconnect,
    {NULL,NULL},                       /* driver_list */ 
    NULL,                              /* *ioctl */
    isoloop_id_table,
    NULL,                              /* suspend */
    NULL                               /* resume */
};

extern int scan_periodic(void);

extern UINT32 *_HCCA_Block;

void  TestLoop()
{
    INT    		j, k;
    UINT32		t0, t1;
    UINT32		last_frame_number;
    INT			nStatus;

	_nCmpBuffHead = _nCmpBuffTail = 0;
	while ((volatile)_ptCY3684Dev == NULL)
#ifdef ECOS
		Hub_CheckIrqEvent(0);;
#else	
		Hub_CheckIrqEvent();;
#endif		

	memset((UINT8 *)&tUrbIsoOut, 0, sizeof(tUrbIsoOut));
    tUrbIsoOut.dev = _ptCY3684Dev;
    tUrbIsoOut.context = (VOID *)_ptCY3684Dev;
    tUrbIsoOut.pipe = usb_sndisocpipe(_ptCY3684Dev, 2);
    tUrbIsoOut.transfer_flags = USB_ISO_ASAP;
    tUrbIsoOut.transfer_buffer = (UINT8 *)((UINT32)_u8DataBufferOut);
    tUrbIsoOut.complete = iso_out_irq;
    tUrbIsoOut.number_of_packets = FRAMES_PER_DESC;
    tUrbIsoOut.transfer_buffer_length = 3072 * FRAMES_PER_DESC;
	tUrbIsoOut.interval = 8;

    for (j=k=0; j < FRAMES_PER_DESC; j++, k += _nTestPacketLen) 
    {
        tUrbIsoOut.iso_frame_desc[j].offset = k;
        tUrbIsoOut.iso_frame_desc[j].length = _nTestPacketLen;
		memcpy(&_u8DataBufferOut[k], 
				(UINT8 *)(0x8000+_nCmpBuffTail), 
				_nTestPacketLen);
		memcpy(&_u8CompareBuff[_nCmpBuffTail], 
				(UINT8 *)(0x8000+_nCmpBuffTail), 
				_nTestPacketLen);
		_nCmpBuffTail = (_nCmpBuffTail+_nTestPacketLen) % COMPARE_BUFF_SIZE;
    }

   	nStatus = USB_SubmitUrb(&tUrbIsoOut);
   	if (nStatus < 0)
   	{
   		printf("Submit iso-out URB error, %d\n", nStatus);
   		return;
   	}
   	else
   		printf("Submit iso-out URB OK\n");

	memset((UINT8 *)&tUrbIsoIn, 0, sizeof(tUrbIsoIn));
    tUrbIsoIn.dev = _ptCY3684Dev;
    tUrbIsoIn.context = (VOID *)_ptCY3684Dev;
    tUrbIsoIn.pipe = usb_rcvisocpipe(_ptCY3684Dev, 6);
    tUrbIsoIn.transfer_flags = USB_ISO_ASAP;
    tUrbIsoIn.transfer_buffer = (UINT8 *)((UINT32)_u8DataBufferIn);
    tUrbIsoIn.complete = iso_in_irq;
    tUrbIsoIn.number_of_packets = FRAMES_PER_DESC;
    tUrbIsoIn.transfer_buffer_length = 3072 * FRAMES_PER_DESC;
	tUrbIsoIn.interval = 8;
    for (j=k=0; j < FRAMES_PER_DESC; j++, k += _nTestPacketLen) 
    {
        tUrbIsoIn.iso_frame_desc[j].offset = k;
        tUrbIsoIn.iso_frame_desc[j].length = _nTestPacketLen;
    }
    if (USB_SubmitUrb(&tUrbIsoIn))
    	printf("\nsubmit irq in - failed!\n");
		
	last_frame_number = _HCCA_Block[32];
	
	t0 = t1 = sysGetTicks(TIMER0);
	while(1)
	{
		if (sysGetTicks(TIMER0) - t1 > 100)
		{
			int  tmp;

			tmp = (sysGetTicks(TIMER0) - t0) / 100;
			printf("%02d:%02d:%02d - IN: %d KB, OUT: %d KB (%d)\n", 
					tmp / 3600, (tmp % 3600) / 60, tmp % 60,
					_uDataCountIN/1024, _uDataCountOUT/1024, USB_available_memory());
			t1 = sysGetTicks(TIMER0);
		}
	}
	
	
	
	while (1)
	{
		t1 = sysGetTicks(TIMER0);
		while (sysGetTicks(TIMER0) - t1 < 100)
			;
		//sysDisableInterrupt(IRQ_USBH);
		//scan_periodic();
		//sysEnableInterrupt(IRQ_USBH);
		t1 = (sysGetTicks(TIMER0) - t0)/100;
		printf("%02d:%02d:%02d - IN: %d KB, OUT: %d KB (%d)\n", 
				t1 / 3600, (t1 % 3600) / 60, t1 % 60,
				_uDataCountIN/1024, _uDataCountOUT/1024, USB_available_memory());
	}
}


int main()
{
#ifndef ECOS	
    WB_UART_T 	uart;
#endif    
	WB_PLL_T 	sysClock;

	/* CACHE_ON	*/
	//sysInvalidCache();
	//sysEnableCache(CACHE_WRITE_BACK);
	//sysSetCachePages((UINT32)_BufferOutNonCache, TRANSFER_BUFF_SIZE, CACHE_DISABLE);
	//sysSetCachePages((UINT32)_BufferInNonCache, TRANSFER_BUFF_SIZE, CACHE_DISABLE);
	_u8DataBufferOut = (UINT8 *)((UINT32)_BufferOutNonCache | 0x80000000);
	_u8DataBufferIn = (UINT8 *)((UINT32)_BufferInNonCache | 0x80000000);

	sysClock.pll0 = PLL_200MHZ;				//PLL0 output clock
	sysClock.pll1 = PLL_133MHZ;				//PLL1 output clock
	sysClock.cpu_src = CPU_FROM_PLL0;		//Select CPU clock source
	sysClock.ahb_clk = AHB_CPUCLK_1_2;		//Select AHB clock divider
	sysClock.apb_clk = APB_AHB_1_2;			//Select APB clock divider
	sysSetPLLConfig(&sysClock);				//Call system function call

#ifndef ECOS	
	/* initialize UART */
	outpw(REG_MFSEL, inpw(REG_MFSEL) | 0x100);
    uart.uiFreq = SYSTEM_CLOCK;
    uart.uiBaudrate = UART_BAUD_RATE;
    uart.uiDataBits = WB_DATA_BITS_8;
    uart.uiStopBits = WB_STOP_BITS_1;
    uart.uiParity = WB_PARITY_NONE;
    uart.uiRxTriggerLevel = LEVEL_1_BYTE;
    sysInitializeUART(&uart);
	printf("UART initialized\n");

	sysSetTimerReferenceClock (TIMER0, SYSTEM_CLOCK);
	sysStartTimer(TIMER0, 100, PERIODIC_MODE);
#endif

	InitUsbSystem();       
    USB_RegisterDriver(&isoloop_driver);
	TestLoop();
	return 0;
}


#ifndef ECOS	
/*
 * standalone.c - minimal bootstrap for C library
 * Copyright (C) 2000 ARM Limited.
 * All rights reserved.
 */

/*
 * RCS $Revision: 1 $
 * Checkin $Date: 08/08/22 9:59p $ 0
 * Revising $Author: Mncheng $
 */

/*
 * This code defines a run-time environment for the C library.
 * Without this, the C startup code will attempt to use semi-hosting
 * calls to get environment information.
 */
 extern unsigned int Image$$ZI$$Limit;

void _sys_exit(int return_code)
{
label:  goto label; /* endless loop */
}

void _ttywrch(int ch)
{
    char tempch = (char)ch;
    (void)tempch;
}



__value_in_regs struct R0_R3 {unsigned heap_base, stack_base, heap_limit, stack_limit;} 
    __user_initial_stackheap(unsigned int R0, unsigned int SP, unsigned int R2, unsigned int SL)
{
    struct R0_R3 config;

    config.heap_base = (unsigned int)&Image$$ZI$$Limit;
    config.stack_base = 0x800000; //Stack base;

/*
To place heap_base directly above the ZI area, use:
    extern unsigned int Image$$ZI$$Limit;
    config.heap_base = (unsigned int)&Image$$ZI$$Limit;
(or &Image$$region_name$$ZI$$Limit for scatterloaded images)

To specify the limits for the heap & stack, use e.g:
    config.heap_limit = SL;
    config.stack_limit = SL;
*/

    return config;
}

/* end of file standalone.c */

/* end of file standalone.c */
#endif